#include "cesium/stages/timedmonitor.hpp"
#include "cesium/stages/filewriter.hpp"
#include "cesium/pipeline.hpp"

#include "asiotimer.hpp"

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

using namespace Cesium;

/** Templated timed variable monitor -> disk pipeline.
 *  ( Timed Variable Monitor ) -> ( Raw File Dump )
 */
template<typename VarType, typename TimerType>
class TimedVariableDiskMonitor : public Pipeline {
public:
    typedef typename TimerType::DurationType DurationType;

    TimedVariableDiskMonitor(VarType* var, TimerType timer, DurationType period, const std::string& filename) {
        FileWriterStage* writerStage = new FileWriterStage(filename);
        add(writerStage);

        TimedMonitorStage<VarType, TimerType>* monitorStage = new TimedMonitorStage<VarType, TimerType>(var, timer, period, writerStage);
        add(monitorStage);
        monitorStage->unref();

        writerStage->unref();
    }
}; // class TimedVariableDiskMonitor


void end_prog(const boost::system::error_code& error, boost::asio::io_service* service) {
    service->stop();
}

void inc_counter_cb(const boost::system::error_code& error, uint32* counter, boost::asio::deadline_timer* timer) {
    (*counter)++;
    timer->expires_from_now( boost::posix_time::milliseconds(250) );
    timer->async_wait( boost::bind(inc_counter_cb, _1, counter, timer) );
}

int main(int argc, char** argv) {
    // the counter we'll monitor
    uint32 counter = 0;

    boost::asio::io_service io_service;

    // increment the counter periodically
    boost::asio::deadline_timer inc_counter_timer(io_service);
    inc_counter_cb(boost::system::error_code(), &counter, &inc_counter_timer);

    // end the program after a few seconds
    boost::asio::deadline_timer end_prog_timer(io_service, boost::posix_time::seconds(6));
    end_prog_timer.async_wait( boost::bind(end_prog, _1, &io_service) );

    // set up the timed variable monitor, which writes the current value to disk periodically
    TimedVariableDiskMonitor<uint32, AsioTimer> monitor(&counter, AsioTimer(io_service), boost::posix_time::seconds(1), "timed_counter_monitor.bin");

    io_service.run();

    return 0;
}
